<template>
  <div class="graph-analysis-section">
    <graph-analysis-header :project-id="projectId" />
    <div ref="analysisWindow" class="analysis-section-window">
      <graph-analysis-left-bar />
      <div class="analysis-section-inner">
        <div class="graph-drawing-area">
          <!-- 缩放 -->
          <graph-zoom-control
            :zoom-level="scaleLevel"
            :zoom-type="scaleType"
            @scale="changeScale"
            @scale-type-change="handleScaleTypeChange"
            @fit-view="fitView"
          />

          <!-- graph -->
          <div
            id="graph-analysis-root"
            ref="graphAnalysisRoot"
            class="graph-analysis-root"
          ></div>
          <!-- loading -->
          <div
            v-show="graphAnalysisLoading"
            class="analysis-query-loading"
            :style="{ height: `calc(100% - ${dataTableHeight}px)` }"
          >
            <a-spin
              :tip="graphAnalysisLoadingText"
              size="large"
              :delay="1000"
            ></a-spin>
          </div>
          <!-- no graph data -->
          <div
            v-show="isGraphDataEmpty"
            class="analysis-query-no-data"
            :style="{ height: `calc(100% - ${dataTableHeight}px)` }"
          >
            <img alt="" src="@/assets/view/empty2.png" />
            <p>当前暂无图数据</p>
          </div>
          <!-- graph info -->
          <div v-show="graphInfoIsShow" class="graph-info">
            <div>
              <span class="graph-info-title">节点：</span>
              <span>{{ nodeNumber }}</span>
            </div>
            <div>
              <span class="graph-info-title">边：</span>
              <span>{{ edgeNumber }}</span>
            </div>
            <span>{{ isDirected ? '有向图' : '无向图' }}</span>
          </div>
          <!-- select circle -->
          <svg
            v-show="circleRadius > 1 && mouseInCanvas"
            class="select-circle"
            :style="{
              position: 'absolute',
              left: mousePosition[0] - circleRadius + 'px',
              top: mousePosition[1] - circleRadius + 'px',
              width: '100%',
              height: '100%',
            }"
          >
            <circle
              :cx="circleRadius"
              :cy="circleRadius"
              :r="circleRadius"
              fill="#eaeaea"
              :style="{ opacity: 0.3 }"
            />
          </svg>
        </div>
        <!-- 数据详情  -->
        <div
          class="analysis-section-footer"
          :class="{ 'footer-transition': footerTransition }"
          :style="{ height: dataTableHeight + 'px' }"
        >
          <!-- resize -->
          <div
            class="table-resize-bar"
            @mousedown="resizeDrawerWidth($event)"
          ></div>
          <!-- toggle -->
          <div class="handler" @click="onChange">
            <a-icon :type="tableSwitch ? 'down' : 'up'" />
          </div>
          <!-- content  -->
          <analysis-footer :footer-height="dataTableHeight" />
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import GraphAnalysisHeader from '@/components/graph-analysis/GraphAnalysisHeader.vue'
import GraphAnalysisLeftBar from '@/components/graph-analysis/GraphAnalysisLeftBar.vue'
import GraphAnalysis from '@/store/modules/graphanalysis'
import AnalysisFooter from '@/components/graph-analysis/analysis-footer/AnalysisFooter.vue'
import GraphZoomControl from '@/components/graph/model-analysis/GraphZoomControl.vue'
import { cloneDeep, debounce } from 'lodash'
import G6 from '@antv/g6'

import elementResizeDetectorMaker from 'element-resize-detector'
import {
  getLightColorFromBasedColor,
  getDarkColor,
  getLightColor,
} from '@/components/graph/graph-util'
import { loadDqueryLoadStatusata } from '@/api/graph-analysis'
import {
  BASIC_ARC,
  CIRCULAR_ARC,
  ROTATING,
  SCALE,
  NOVERLAP,
  // LABEL_ADJUST,
} from '@/components/graph-analysis/right-drawer/style-layout-config/style-layout'
import registerCircularArcG6Layout from '@/components/graph-analysis/right-drawer/style-layout-config/circular-arc-layout'
import registerBasicArcG6Layout from '@/components/graph-analysis/right-drawer/style-layout-config/basic-arc-layout'
import registerRotateLayoutTool from '@/components/graph-analysis/right-drawer/style-layout-config/rotate-layout-tool'
import registerScaleLayout from '@/components/graph-analysis/right-drawer/style-layout-config/scale-layout'
import registerNoverlapLayout from '@/components/graph-analysis/right-drawer/style-layout-config/noverlap-layout'
import { isItemIntersecPolygon } from './utll'

@Component({
  components: {
    GraphAnalysisHeader,
    GraphAnalysisLeftBar,
    AnalysisFooter,
    GraphZoomControl,
  },
})
export default class GraphAnalysisSection extends Vue {
  @Prop({
    type: Number,
    default: 0,
  })
  projectId!: number

  private analysisGraph: any // 画布
  private tableSwitch: boolean = false // 图数据详情
  private footerTransition: boolean = true // footer 过渡， 拖动时无过渡

  private data: any = {} // 图数据
  private isGraphDataEmpty: boolean = false

  private transformedData: any = {} // 转换后的数据
  private isNodesWithPosition: boolean = false

  public get graphAnalysisLoading() {
    return GraphAnalysis.graphAnalysisLoading
  }

  public get graphAnalysisLoadingText() {
    return GraphAnalysis.graphAnalysisLoadingText
  }

  private currentKeyboard: string = '' // 用户当前的键盘按键

  private mousePosition: Array<number> = [0, 0] // 鼠标位置
  private dragStartPosition: Array<number> = [0, 0] // 拖拽起始位置
  private nodeFrom: any = null // 最短路径，起始节点
  private nodeTo: any = null // 最短路径，终点
  private mouseInCanvas = false
  private dragTime = 0
  private preventDrag = false

  private nodeNumber: number = 0
  private edgeNumber: number = 0
  private isDirected: boolean = false

  private selectedNodes: Array<any> = []

  public mounted() {
    this.watchGraphRootSize()
    document.addEventListener('mouseup', this.globalMouseUp)
    document.addEventListener('keyup', this.globalKeyUp)
    document.addEventListener('keydown', this.globalKeyDown)
  }

  // 当前graphId
  public get graphId() {
    return GraphAnalysis.activeTabId
  }

  // 所有图的缩放比例
  public get graphScales() {
    return GraphAnalysis.graphScales
  }

  // 监听 graphId 变化
  @Watch('graphId')
  public graphIdChange(newGraphId: number | null, oldGraphId: number | null) {
    this.nodeNumber = 0
    this.edgeNumber = 0
    // 点击切换 tab 销毁现存的轮询任务
    if (newGraphId && !this.loadId) {
      this.successOrFailClearPollingTask()
      this.queryGraphById()
    } else {
      this.setLoadGraphDataInterval() // 由图构建加载过来
    }

    // 保存当前图的缩放比例
    const index = GraphAnalysis.tabsData.findIndex((tab: any) => {
      return tab.id === oldGraphId
    })

    if (index >= 0) {
      const graphScales = this.graphScales.slice()
      graphScales[index] = {
        scaleType: this.scaleType,
        scaleLevel: this.scaleLevel,
      }
      GraphAnalysis.setGraphScales(graphScales)
    }
  }

  public loadGraphInterval: any = null // （图构建加载到图分析）轮询任务 Interval
  // 加载轮询的id
  public get loadId() {
    return GraphAnalysis.loadId
  }

  /**
   * （图构建加载到图分析, 载入数据） 定时任务
   */
  public setLoadGraphDataInterval() {
    GraphAnalysis.setGraphAnalysisLoadingText('加载中，请等待...')
    GraphAnalysis.setGraphAnalysisLoading(true)
    const data: any = {
      projectId: this.projectId,
      graphId: GraphAnalysis.activeTabId,
      id: this.loadId,
    }
    // 定时器查询轮询
    clearInterval(this.loadGraphInterval)
    GraphAnalysis.setLoadIdId(null) // 上面用过后立即清除
    this.loadStatus(data)
    this.loadGraphInterval = setInterval(() => {
      this.loadStatus(data)
    }, 1000)
  }

  /**
   * （图构建加载到图分析， 载入数据） 查询图加载状态
   */
  public loadStatus(data: { projectId: number; graphId: number; id: number }) {
    loadDqueryLoadStatusata({ data }).then((response: any) => {
      if (response.data.code === 100) {
        const Status = response.data.result.status
        if (Status === 'FAIL') {
          GraphAnalysis.setCurrentTabLoadGraphDataIng(1) // 加载完成 - 失败
          this.successOrFailClearPollingTask()
          const errorInfo: any = JSON.parse(response.data.result.log)
          if (errorInfo.errorMsg.length > 0) {
            this.$message.error(errorInfo.errorMsg)
          } else {
            this.$message.error('加载失败，请确认数据！')
          }
          GraphAnalysis.setCurrentLoadingGraphDataTabId(null)
        } else if (Status === 'SUCCESS') {
          this.queryGraphById()
          GraphAnalysis.setCurrentTabLoadGraphDataIng(1) // 加载完成 - 成功
          this.successOrFailClearPollingTask()
          this.$message.success('图数据加载成功!')
          GraphAnalysis.setCurrentLoadingGraphDataTabId(null)
        }
      } else {
        this.$message.error('加载失败，请检查！')
        GraphAnalysis.setCurrentTabLoadGraphDataIng(1) // 加载错误
        this.successOrFailClearPollingTask()
        GraphAnalysis.setCurrentLoadingGraphDataTabId(null)
      }
    })
  }

  /**
   * （图构建加载到图分析， 载入数据）轮询任务成功或者失败清空 定时任务-loading-loadId
   */
  public successOrFailClearPollingTask() {
    this.delayCloseLoading(500) // 延时关闭 loading
    clearInterval(this.loadGraphInterval)
  }

  /**
   * 延时关闭 loading
   */
  public delayCloseLoading(delayTime: number) {
    setTimeout(() => {
      GraphAnalysis.setGraphAnalysisLoading(false)
    }, delayTime)
  }

  // （数据管理的图数据）是否正在加载数据
  public get currentTabLoadGraphDataIng() {
    return GraphAnalysis.currentTabLoadGraphDataIng
  }

  /**
   * 数据管理的图数据，选择后确认加载，
   */
  @Watch('currentTabLoadGraphDataIng')
  public currentTabLoadGraphDataIngWatch(status: number) {
    if (status === 2) {
      this.setLoadGraphDataInterval()
    }
  }

  /**
   * 更新图数据
   * warningText，在过滤后穿参调用
   */
  public queryGraphById() {
    this.isGraphDataEmpty = false

    GraphAnalysis.setGraphAnalysisLoadingText('加载中，请等待...')
    GraphAnalysis.setGraphAnalysisLoading(true)

    if (this.analysisGraph) {
      this.analysisGraph.clear()
    }
    const data = {
      projectId: GraphAnalysis.projectId,
      graphId: GraphAnalysis.activeTabId,
    }
    this.$nextTick(() => {
      GraphAnalysis.queryGraph(data)
        .then((response: any) => {
          if (response.data.code === 100) {
            const {
              nodes,
              links,
              categories,
              edges,
              actionContext,
              graphInfo,
            } = response.data.result
            GraphAnalysis.setGraphDirected(graphInfo.directed) // 存储图是否为有向图
            GraphAnalysis.setCategories(categories) // 存储节点分类 - 属性着色
            GraphAnalysis.setEdges(edges) // 存储边的分类 - 数据详情
            this.data = {
              nodes,
              edges: links,
            }

            if (this.data.nodes.length === 0) {
              this.isGraphDataEmpty = true
              return
            }
            const newDataFirstNode: any = cloneDeep(nodes[0])
            // 检查节点是否带有坐标信息
            this.isNodesWithPosition =
              'x' in newDataFirstNode && 'y' in newDataFirstNode
            // 转换数据格式，为边和节点增加样式
            this.transformData()

            // g6 已存在
            if (this.analysisGraph) {
              if (this.isNodesWithPosition) {
                this.analysisGraph.destroyLayout()
              } else {
                this.analysisGraph.updateLayout(this.randomLayout)
              }
              this.analysisGraph.changeData(this.transformedData) // 转化后的图数据
              this.analysisGraph.fitCenter()
              this.analysisGraph.setMode('select')
              this.analysisGraph.getNodes().forEach((node: any) => {
                this.analysisGraph.setItemState(node, 'default', true)
              })
              GraphAnalysis.setManualAction(false)
              this.changeLabelSettings(this.labelSettings)
              this.changeEdgeSettings(this.edgeSettings)
            } else {
              this.initGraphAnalysis()
            }

            // graph info
            this.nodeNumber = graphInfo.numOfNodes
            this.edgeNumber = graphInfo.numOfLinks
            this.isDirected = graphInfo.directed

            // 恢复actionContext
            if (actionContext) {
              GraphAnalysis.setSelectedTool(actionContext.selectedTool)
              GraphAnalysis.setSelectRadius(actionContext.selectRadius)
              GraphAnalysis.setSelectNeighbors(actionContext.selectNeighbors)
              GraphAnalysis.setLassoNeighbors(actionContext.lassoNeighbors)
              GraphAnalysis.setColorRadius(actionContext.colorRadius)
              GraphAnalysis.setSelectedColor(actionContext.selectedColor)
              GraphAnalysis.setFormatColor(actionContext.formatColor)
              GraphAnalysis.setFormatType(actionContext.formatType)
              GraphAnalysis.setRedrawColor(actionContext.redrawColor)
              GraphAnalysis.setLabelIsShow(actionContext.labelIsShow)
              GraphAnalysis.setLabelFontType(actionContext.labelFontType)
              GraphAnalysis.setLabelFontSize(actionContext.labelFontSize)
              GraphAnalysis.setLabelColor(actionContext.labelColor)
              GraphAnalysis.setEdgeIsShow(actionContext.edgeIsShow)
              GraphAnalysis.setEdgeLabelIsShow(actionContext.edgeLabelIsShow)
              GraphAnalysis.setEdgeColorType(actionContext.edgeColorType)
              GraphAnalysis.setEdgeColor(actionContext.edgeColor)
              GraphAnalysis.setHeatmapColor(actionContext.heatmapColor)
            } else {
              GraphAnalysis.setSelectedTool('')
              GraphAnalysis.setSelectRadius(1)
              GraphAnalysis.setSelectNeighbors(true)
              GraphAnalysis.setLassoNeighbors(true)
              GraphAnalysis.setColorRadius(1)
              GraphAnalysis.setSelectedColor('#D2D2D2')
              GraphAnalysis.setFormatColor('#D2D2D2')
              GraphAnalysis.setFormatType('both')
              GraphAnalysis.setRedrawColor('#D2D2D2')
              GraphAnalysis.setLabelIsShow('show-outer')
              GraphAnalysis.setLabelFontType('self')
              GraphAnalysis.setLabelFontSize(12)
              GraphAnalysis.setLabelColor('#000000')
              GraphAnalysis.setEdgeIsShow('show')
              GraphAnalysis.setEdgeLabelIsShow('notshow')
              GraphAnalysis.setEdgeColorType('self')
              GraphAnalysis.setEdgeColor('#D9D9D9')
              GraphAnalysis.setHeatmapColor('#a4abfb')
            }

            // 重新加载图时，还原该图之前操作的缩放比例
            const index = GraphAnalysis.tabsData.findIndex((tab: any) => {
              return tab.id === this.graphId
            })

            if (index >= 0) {
              const { scaleType, scaleLevel } = this.graphScales[index]
              this.scaleLevel = scaleLevel
              this.scaleType = scaleType
              this.changeScale(scaleLevel)
            }
          }
        })
        .finally(() => {
          this.delayCloseLoading(500) // 延时关闭 loading
        })
    })
  }

  public watchGraphRootSize() {
    const rootErd = elementResizeDetectorMaker()
    const graphAnalysisRoot = this.$refs.graphAnalysisRoot as HTMLElement
    rootErd.listenTo(graphAnalysisRoot, () => {
      this.analysisGraphDebounce()
    })
    this.$once('hook:beforeDestroy', () => {
      rootErd.removeAllListeners(graphAnalysisRoot)
    })
  }

  private analysisGraphDebounce = debounce(
    () => this.analysisGraphResize(),
    500
  )

  public analysisGraphResize() {
    const graphElement = this.$refs.graphAnalysisRoot as HTMLElement

    if (this.analysisGraph) {
      this.analysisGraph.changeSize(
        graphElement.offsetWidth,
        graphElement.offsetHeight
      )
    }

    GraphAnalysis.setGraphAnalysisRootSize({
      width: graphElement?.offsetWidth,
      height: graphElement?.offsetHeight,
    })
  }

  public beforeDestroy() {
    document.removeEventListener('mouseup', this.globalMouseUp)
  }

  public get graphInfoIsShow() {
    return GraphAnalysis.graphInfoIsShow
  }

  public get selectedTool() {
    return GraphAnalysis.selectedTool
  }

  public get circleRadius() {
    if (this.selectedTool === '选择') {
      return GraphAnalysis.selectRadius
    }
    if (this.selectedTool === '拖动') {
      return GraphAnalysis.dragRadius
    }
    if (this.selectedTool === '着色') {
      return GraphAnalysis.colorRadius
    }
    return 0
  }

  public get needRedraw() {
    return GraphAnalysis.needRedraw
  }

  public get labelSettings() {
    return {
      labelIsShow: GraphAnalysis.labelIsShow,
      labelFontType: GraphAnalysis.labelFontType,
      labelFontSize: GraphAnalysis.labelFontSize,
      labelColorType: GraphAnalysis.labelColorType,
      labelColor: GraphAnalysis.labelColor,
    }
  }

  public get deleteItem() {
    return GraphAnalysis.deleteItem
  }

  public get edgeSettings() {
    return {
      edgeIsShow: GraphAnalysis.edgeIsShow,
      edgeColorType: GraphAnalysis.edgeColorType,
      edgeColor: GraphAnalysis.edgeColor,
      edgeLabelIsShow: GraphAnalysis.edgeLabelIsShow,
    }
  }

  public get undoRedoAction() {
    return GraphAnalysis.undoRedoAction
  }

  public get searchResult() {
    return GraphAnalysis.searchResult
  }

  public get neighborState() {
    return {
      selectNeighbors: GraphAnalysis.selectNeighbors,
      lassoNeighbors: GraphAnalysis.lassoNeighbors,
    }
  }

  public get needSave() {
    return GraphAnalysis.needSave
  }

  public get heatmapResult() {
    return GraphAnalysis.heatmapResult
  }

  /**
   * 根据当前选择的工具设置mode
   */
  public get graphMode() {
    if (
      this.selectedTool === '' ||
      (this.selectedTool === '选择' && this.circleRadius <= 1)
    ) {
      return 'select'
    }
    if (this.selectedTool === '选择' && this.circleRadius > 1) {
      return 'circleSelect'
    }
    if (this.selectedTool === '框选') {
      return 'brush'
    }
    if (this.selectedTool === '套索') {
      return 'lasso'
    }
    if (this.selectedTool === '最短路径') {
      return 'shortestPath'
    }
    if (this.selectedTool === '着色' && this.circleRadius <= 1) {
      return 'color'
    }
    if (this.selectedTool === '着色' && this.circleRadius > 1) {
      return 'circleColor'
    }
    if (this.selectedTool === '格式刷') {
      return 'formatBrush'
    }
    if (this.selectedTool === '热区') {
      return 'heatmap'
    }

    return 'default'
  }

  @Watch('graphMode')
  onChangeGraphMode(newValue: string) {
    if (newValue === 'shortestPath') {
      this.clearSelectedNodes(this.analysisGraph)
      this.clearSelectedEdges(this.analysisGraph)
    }
    this.analysisGraph.setMode(newValue)
  }

  @Watch('selectedTool')
  onChangeSelectedTool(newValue: string) {
    if (newValue === '居中') {
      const width = this.analysisGraph.getWidth()
      const height = this.analysisGraph.getHeight()
      const zoom = this.analysisGraph.getZoom()
      this.analysisGraph.fitCenter()
      this.analysisGraph.zoomTo(zoom, { x: width / 2, y: height / 2 })
      GraphAnalysis.setSelectedTool('')
    } else if (newValue === '重设颜色') {
      this.saveChangedData([], [])
      this.setNodeColor(
        this.analysisGraph,
        this.analysisGraph.getNodes(),
        GraphAnalysis.redrawColor
      )
    }
  }

  @Watch('needRedraw')
  redrawNodesColor(newValue: boolean) {
    if (newValue) {
      this.setNodeColor(
        this.analysisGraph,
        this.analysisGraph.getNodes(),
        GraphAnalysis.redrawColor
      )
      // this.saveChangedData(this.analysisGraph.getNodes())
      GraphAnalysis.setNeedRedraw(false)
    }
  }

  @Watch('labelSettings')
  changeLabelSettings(settings: any) {
    this.analysisGraph.getNodes().forEach((node: any) => {
      const nodeColor = node.getStateStyle('default').fill
      const nodeRadius = node.getOriginStyle().r

      node.update({
        labelCfg: {
          position:
            settings.labelIsShow === 'show-inner' ||
            settings.labelIsShow === 'highlightshow-inner'
              ? 'center'
              : 'bottom',
          offset:
            settings.labelIsShow === 'show-inner' ||
            settings.labelIsShow === 'highlightshow-inner'
              ? 0
              : nodeRadius / 2 + 3,
          style: {
            fontSize:
              settings.labelFontType === 'node'
                ? nodeRadius
                : settings.labelFontSize,
            fill:
              settings.labelColorType === 'node'
                ? nodeColor
                : settings.labelColor,
            textAlign: 'center',
            textBaseline: 'middle',
          },
        },
      })

      const container = node.getContainer()
      const children = container.get('children')
      children.forEach((child: any) => {
        if (child.cfg.type === 'text') {
          if (
            settings.labelIsShow === 'show-inner' ||
            settings.labelIsShow === 'show-outer' ||
            ((settings.labelIsShow === 'highlightshow-inner' ||
              settings.labelIsShow === 'highlightshow-outer') &&
              node.hasState('highlight'))
          ) {
            child.show()
          } else {
            child.hide()
          }
        }
      })
    })
    if (GraphAnalysis.isManualAction) {
      this.saveChangedData(this.analysisGraph.getNodes())
    }
  }

  @Watch('deleteItem')
  async deleteSeletedItems(value: boolean) {
    if (value) {
      const nodes = this.analysisGraph.findAllByState('node', 'highlight')
      const seletedNodeIds = nodes.map((node: any) => node.getModel().id)
      const selectedEdgeIds: Array<string> = []
      nodes.forEach((node: any) => {
        node.getEdges().forEach((edge: any) => {
          const { id } = edge.getModel()
          if (!selectedEdgeIds.includes(id)) {
            selectedEdgeIds.push(id)
          }
        })
        this.analysisGraph.removeItem(node)
      })

      const deleteItemsResponse: any = await GraphAnalysis.deleteGraphItems({
        projectId: this.projectId,
        graphId: this.graphId,
        nodeIds: seletedNodeIds,
        linkIds: selectedEdgeIds,
      })
      GraphAnalysis.setDeleteItem(false)
      if (deleteItemsResponse.success) {
        await GraphAnalysis.queryAvailableCategory() // 涉及到节点数量变化的， 更新节点/边 的分类信息， 进而更新图数据表和节点样式中的着色和排序方案
        this.nodeNumber -= seletedNodeIds.length
        this.edgeNumber -= selectedEdgeIds.length
      }
    }
  }

  @Watch('edgeSettings')
  changeEdgeSettings(settings: any) {
    this.analysisGraph.getEdges().forEach((edge: any) => {
      const sourseColor = edge.getSource().getStateStyle('default').fill
      const model = edge.getModel()

      edge.update({
        style: {
          stroke:
            settings.edgeColorType === 'node' && model.directed
              ? sourseColor
              : settings.edgeColor,
          lineWidth: model.weight ? model.weight : 1,
          endArrow: model.directed,
        },
      })

      const container = edge.getContainer()
      const children = container.get('children')
      children.forEach((child: any) => {
        if (child.cfg.type === 'text') {
          if (settings.edgeLabelIsShow === 'show') {
            child.show()
          } else {
            child.hide()
          }
        }
      })

      if (settings.edgeIsShow === 'show') {
        edge.show()
      } else {
        edge.hide()
      }
    })
    if (GraphAnalysis.isManualAction) {
      this.saveChangedData([], this.analysisGraph.getEdges())
    }
  }

  @Watch('undoRedoAction')
  undoRedoActionChanged(action: any) {
    if (!action) {
      return
    }
    if (action.actionType === 'update') {
      action.actionNodes.forEach((model: any) => {
        this.analysisGraph.updateItem(model.id, {
          ...model,
          stateStyles: {
            default: {
              lineWidth: 4,
              fill: model.style.fill,
              stroke: model.style.fill,
            },
            hover: {
              lineWidth: 4,
              stroke: getLightColor(model.style.fill, 0.5),
            },
            highlight: {
              lineWidth: 4,
              stroke: getDarkColor(model.style.fill, 0.2),
              fill: model.style.fill,
            },
            dark: {
              lineWidth: 4,
              fill: getLightColor(model.style.fill, 0.7),
              stroke: getLightColor(model.style.fill, 0.7),
            },
          },
        })
      })
      action.actionLinks.forEach((model: any) => {
        this.analysisGraph.updateItem(model.id, model)
      })

      if (action.actionContext && action.actionContext.selectedTool) {
        GraphAnalysis.setSelectedTool(action.actionContext.selectedTool)
        GraphAnalysis.setSelectRadius(action.actionContext.selectRadius)
        GraphAnalysis.setSelectNeighbors(action.actionContext.selectNeighbors)
        GraphAnalysis.setLassoNeighbors(action.actionContext.lassoNeighbors)
        GraphAnalysis.setColorRadius(action.actionContext.colorRadius)
        GraphAnalysis.setSelectedColor(action.actionContext.selectedColor)
        GraphAnalysis.setFormatColor(action.actionContext.formatColor)
        GraphAnalysis.setFormatType(action.actionContext.formatType)
        GraphAnalysis.setRedrawColor(action.actionContext.redrawColor)
        GraphAnalysis.setLabelIsShow(action.actionContext.labelIsShow)
        GraphAnalysis.setLabelFontType(action.actionContext.labelFontType)
        GraphAnalysis.setLabelFontSize(action.actionContext.labelFontSize)
        GraphAnalysis.setLabelColor(action.actionContext.labelColor)
        GraphAnalysis.setEdgeIsShow(action.actionContext.edgeIsShow)
        GraphAnalysis.setEdgeLabelIsShow(action.actionContext.edgeLabelIsShow)
        GraphAnalysis.setEdgeColorType(action.actionContext.edgeColorType)
        GraphAnalysis.setEdgeColor(action.actionContext.edgeColor)
        GraphAnalysis.setHeatmapColor(action.actionContext.heatmapColor)
      } else {
        GraphAnalysis.setSelectedTool('')
      }

      this.analysisGraph.refreshPositions()
    } else if (
      action.actionType === 'delete' &&
      GraphAnalysis.undoRedoStatus === 'undo'
    ) {
      action.actionNodes.forEach((model: any) => {
        this.analysisGraph.addItem('node', {
          ...model,
          stateStyles: {
            default: {
              lineWidth: 4,
              fill: model.style.fill,
              stroke: model.style.fill,
            },
            hover: {
              lineWidth: 4,
              stroke: getLightColor(model.style.fill, 0.5),
            },
            highlight: {
              lineWidth: 4,
              stroke: getDarkColor(model.style.fill, 0.2),
              fill: model.style.fill,
            },
            dark: {
              lineWidth: 4,
              fill: getLightColor(model.style.fill, 0.7),
              stroke: getLightColor(model.style.fill, 0.7),
            },
          },
        })
      })
      action.actionLinks.forEach((model: any) => {
        this.analysisGraph.addItem('edge', model)
      })
      GraphAnalysis.queryAvailableCategory() // 涉及到节点数量变化的， 更新节点/边 的分类信息， 进而更新图数据表和节点样式中的着色和排序方案
      this.nodeNumber += action.actionNodes.length
      this.edgeNumber += action.actionLinks.length

      // 更新节点和边的设置
      this.changeLabelSettings(this.labelSettings)
      this.changeEdgeSettings(this.edgeSettings)
    } else if (action.actionType === 'graph_filter') {
      // 过滤添加 redo undo
      this.queryGraphById() // 过滤涉及的节点比较多， 所有节点重新画
    } else {
      action.actionLinksIds.forEach((id: string) => {
        this.analysisGraph.removeItem(id)
      })
      action.actionNodesIds.forEach((id: string) => {
        this.analysisGraph.removeItem(id)
      })
      GraphAnalysis.queryAvailableCategory() // 涉及到节点数量变化的， 更新节点/边 的分类信息， 进而更新图数据表和节点样式中的着色和排序方案
      this.nodeNumber -= action.actionNodesIds.length
      this.edgeNumber -= action.actionLinksIds.length
    }
    GraphAnalysis.setGraphAnalysisLoading(false)
    GraphAnalysis.setGraphAnalysisLoading(false)
  }

  @Watch('searchResult')
  searchResultChanged(result: Array<any>) {
    if (this.selectedTool === '搜索') {
      this.clearSelectedNodes(this.analysisGraph)
      if (!result || result.length === 0) {
        this.clearSelectedNodes(this.analysisGraph)
        this.$message.info('搜索结果为空')
      } else {
        const nodes = result.map((id: string) =>
          this.analysisGraph.findById(id)
        )
        this.selectNodes(this.analysisGraph, nodes)
      }
    }
  }

  @Watch('neighborState')
  lassoNeighborsChanged() {
    this.clearSelectedNodes(this.analysisGraph, false)
    this.selectNodes(this.analysisGraph, this.selectedNodes)
  }

  @Watch('needSave')
  toolSettingsChanged(value: boolean) {
    if (value) {
      this.saveChangedData([], [])
      GraphAnalysis.setNeedSave(false)
    }
  }

  @Watch('heatmapResult')
  heatmapStateChanged(value: any) {
    if (!value) {
      return
    }

    this.setNodeColor(
      this.analysisGraph,
      this.analysisGraph.getNodes(),
      '#D9D9D9'
    )
    Object.keys(value).forEach((id: string) => {
      const node = this.analysisGraph.findById(id)
      const color = getLightColor(GraphAnalysis.heatmapColor, value[id] * 0.8)
      this.setNodeColor(this.analysisGraph, [node], color)
    })
    if (this.edgeSettings.edgeColorType === 'node') {
      this.saveChangedData(
        this.analysisGraph.getNodes(),
        this.analysisGraph.getEdges()
      )
    } else {
      this.saveChangedData(this.analysisGraph.getNodes())
    }
  }

  // 布局配置
  private randomLayout: any = {
    type: 'random',
    onLayoutEnd: () => this.randomLayoutDebounce(),
  }

  /**
   * queryGraphById 中 updateLayout()  changeData() 防抖
   */
  private randomLayoutDebounce = debounce(() => this.layoutEnd(), 1500)

  /**
   * 注册节点交互行为
   */
  public registerNodeBehavior() {
    const that = this

    // hover高亮节点
    G6.registerBehavior('node-hover', {
      getEvents() {
        return {
          'node:mouseenter': 'onNodeMouseenter',
          'node:mouseleave': 'onNodeMouseleave',
        }
      },
      onNodeMouseenter(event: any) {
        const Graph: any = this.graph
        Graph.setItemState(event.item, 'hover', true)

        if (
          (GraphAnalysis.labelIsShow === 'highlightshow-inner' ||
            GraphAnalysis.labelIsShow === 'highlightshow-outer') &&
          !event.item.hasState('highlight')
        ) {
          const container = event.item.getContainer()
          const children = container.get('children')
          children.forEach((child: any) => {
            if (child.cfg.type === 'text') {
              child.show()
            }
          })
        }
      },
      onNodeMouseleave(event: any) {
        const Graph: any = this.graph
        Graph.setItemState(event.item, 'hover', false)

        if (
          (GraphAnalysis.labelIsShow === 'highlightshow-inner' ||
            GraphAnalysis.labelIsShow === 'highlightshow-outer') &&
          !event.item.hasState('highlight')
        ) {
          const container = event.item.getContainer()
          const children = container.get('children')
          children.forEach((child: any) => {
            if (child.cfg.type === 'text') {
              child.hide()
            }
          })
        }
      },
    })

    // 单选或多选节点
    G6.registerBehavior('node-select', {
      getDefaultCfg() {
        return {
          multiple: true,
          trigger: 'Control',
        }
      },
      getEvents() {
        return {
          'node:click': 'onNodeClick',
        }
      },
      onNodeClick(event: any) {
        const Graph: any = this.graph
        const selectedItem = event.item

        // 单选时清空之前选中的点
        if (!this.multiple || that.currentKeyboard !== this.trigger) {
          that.clearSelectedNodes(Graph)
        }
        const selectedNodes = [selectedItem]
        if (selectedItem.hasState('highlight')) {
          Graph.clearItemStates(selectedItem)
          Graph.setItemState(selectedItem, 'default', true)
          if (GraphAnalysis.selectNeighbors) {
            Graph.getNeighbors(selectedItem).forEach((node: any) => {
              selectedNodes.push(node)
              Graph.clearItemStates(node)
              Graph.setItemState(node, 'default', true)
            })
          }
          return
        }

        that.selectNodes(Graph, selectedNodes)
      },
    })

    // 圆圈选择
    G6.registerBehavior('circle-select', {
      getEvents() {
        return {
          'node:click': 'onClick',
          'canvas:click': 'onClick',
        }
      },
      onClick(event: any) {
        const Graph: any = this.graph
        that.clearSelectedNodes(Graph)

        const x = event.canvasX
        const y = event.canvasY
        const nodes = Graph.findAll('node', (node: any) => {
          const model = node.get('model')
          const canvasPosition = Graph.getCanvasByPoint(model.x, model.y)
          return (
            Math.sqrt(
              (canvasPosition.x - x) ** 2 + (canvasPosition.y - y) ** 2
            ) <= that.circleRadius
          )
        })
        if (nodes.length > 0) {
          that.selectNodes(Graph, nodes)
        }
      },
    })

    // 拖动单个或多个结点
    G6.registerBehavior('node-drag', {
      getEvents() {
        return {
          'node:dragstart': 'onDragStart',
          'node:drag': 'onDrag',
          'node:dragend': 'onDragEnd',
        }
      },
      onDragStart(event: any) {
        that.dragStartPosition = [event.canvasX, event.canvasY]
      },
      onDrag(event: any) {
        const selectedItem = event.item

        const Graph: any = this.graph
        const zoom = Graph.getZoom()
        const offsetX = (event.canvasX - that.dragStartPosition[0]) / zoom
        const offsetY = (event.canvasY - that.dragStartPosition[1]) / zoom

        if (!selectedItem.hasState('highlight')) {
          const model = selectedItem.get('model')
          model.x += offsetX
          model.y += offsetY
        } else {
          const nodes = Graph.findAllByState('node', 'highlight')
          nodes.forEach((node: any) => {
            const model = node.get('model')
            model.x += offsetX
            model.y += offsetY
          })
        }

        Graph.refreshPositions()
        that.dragStartPosition = [event.canvasX, event.canvasY]
      },
      onDragEnd(event: any) {
        const selectedItem = event.item
        const Graph: any = this.graph

        if (!selectedItem.hasState('highlight')) {
          that.saveChangedData([event.item])
        } else {
          const nodes = Graph.findAllByState('node', 'highlight')
          that.saveChangedData(nodes)
        }
      },
    })

    // 拖动圆圈中的节点
    G6.registerBehavior('circle-drag', {
      getEvents() {
        return {
          'node:dragstart': 'onDragStart',
          'node:drag': 'onDrag',
          'node:dragend': 'onDragEnd',
          'canvas:dragstart': 'onDragStart',
          'canvas:drag': 'onDrag',
          'canvas:dragend': 'onDragEnd',
        }
      },
      onDragStart(event: any) {
        /*
        console.log('drag start')
        if(Date.now()-that.dragTime<1000){
          that.preventDrag = true
          return
        }
        that.preventDrag = false
        */

        const Graph: any = this.graph
        that.clearSelectedNodes(Graph)

        const x = event.canvasX
        const y = event.canvasY
        const nodes = Graph.findAll('node', (node: any) => {
          const model = node.get('model')
          const canvasPosition = Graph.getCanvasByPoint(model.x, model.y)
          return (
            Math.sqrt(
              (canvasPosition.x - x) ** 2 + (canvasPosition.y - y) ** 2
            ) <= that.circleRadius
          )
        })
        if (nodes.length > 0) {
          that.selectNodes(Graph, nodes, true)
        }

        that.dragStartPosition = [event.canvasX, event.canvasY]
      },
      onDrag(event: any) {
        /*
        console.log('dragging')
        if(that.preventDrag){
          return
        }
        */

        const Graph: any = this.graph
        const zoom = Graph.getZoom()
        const offsetX = (event.canvasX - that.dragStartPosition[0]) / zoom
        const offsetY = (event.canvasY - that.dragStartPosition[1]) / zoom

        const nodes = Graph.findAllByState('node', 'highlight')
        nodes.forEach((node: any) => {
          const model = node.get('model')
          model.x += offsetX
          model.y += offsetY
        })
        Graph.refreshPositions()

        if (nodes.length === 0) {
          Graph.translate(
            event.canvasX - that.dragStartPosition[0],
            event.canvasY - that.dragStartPosition[1]
          )
        }

        that.dragStartPosition = [event.canvasX, event.canvasY]
        that.mousePosition = [event.canvasX, event.canvasY]
      },
      onDragEnd() {
        // console.log('drag end')
        const Graph: any = this.graph
        const nodes = Graph.findAllByState('node', 'highlight')
        that.saveChangedData(nodes)
        // that.dragTime = Date.now()
      },
    })

    // 最短路径
    G6.registerBehavior('shortest-path', {
      getEvents() {
        return {
          'node:click': 'onNodeClick',
        }
      },
      async onNodeClick(event: any) {
        const Graph: any = this.graph
        const selectedItem = event.item

        if (!that.nodeFrom) {
          that.clearSelectedNodes(Graph)
          that.clearSelectedEdges(Graph)
          that.nodeFrom = null
          that.nodeTo = null
        }

        Graph.setItemState(selectedItem, 'highlight', true)

        if (!that.nodeFrom) {
          that.nodeFrom = selectedItem.getID()
        } else if (!that.nodeTo) {
          that.nodeTo = selectedItem.getID()

          const response = await GraphAnalysis.queryShortestPath({
            projectId: that.projectId,
            graphId: that.graphId,
            type: 'shortest',
            srcId: that.nodeFrom,
            tarId: that.nodeTo,
          })

          if (!response || response[0].length === 0) {
            return
          }

          const path = response[0]
          const pathNodeMap: any = {}
          path.forEach((id: any) => {
            const pathNode = Graph.findById(id)
            pathNode.toFront()
            Graph.setItemState(pathNode, 'highlight', true)
            pathNodeMap[id] = true
          })

          Graph.getEdges().forEach((edge: any) => {
            const edgeModel = edge.getModel()
            const edgeSource = edgeModel.source
            const edgeTarget = edgeModel.target
            const sourceInPathIdx = path.indexOf(edgeSource)
            const targetInPathIdx = path.indexOf(edgeTarget)
            if (sourceInPathIdx === -1 || targetInPathIdx === -1) {
              return
            }
            if (Math.abs(sourceInPathIdx - targetInPathIdx) === 1) {
              Graph.setItemState(edge, 'highlight', true)
            }
          })

          that.nodeFrom = null
          that.nodeTo = null
        } else {
          that.clearSelectedNodes(Graph)
          that.clearSelectedEdges(Graph)
          that.nodeFrom = null
          that.nodeTo = null
        }
      },
    })

    // 给单个或多个结点着色
    G6.registerBehavior('node-color', {
      getEvents() {
        return {
          'node:click': 'onClick',
        }
      },
      onClick(event: any) {
        const selectedItem = event.item
        const Graph: any = this.graph

        if (!selectedItem.hasState('highlight')) {
          that.setNodeColor(Graph, [selectedItem], GraphAnalysis.selectedColor)
          return
        }

        const nodes = Graph.findAllByState('node', 'highlight')
        that.setNodeColor(Graph, nodes, GraphAnalysis.selectedColor)
      },
    })

    // 圆圈着色
    G6.registerBehavior('circle-color', {
      getEvents() {
        return {
          'node:click': 'onClick',
          'canvas:click': 'onClick',
        }
      },
      onClick(event: any) {
        const Graph: any = this.graph
        that.clearSelectedNodes(Graph)

        const x = event.canvasX
        const y = event.canvasY
        const nodes = Graph.findAll('node', (node: any) => {
          const model = node.get('model')
          const canvasPosition = Graph.getCanvasByPoint(model.x, model.y)
          return (
            Math.sqrt(
              (canvasPosition.x - x) ** 2 + (canvasPosition.y - y) ** 2
            ) <= that.circleRadius
          )
        })

        that.setNodeColor(Graph, nodes, GraphAnalysis.selectedColor)
      },
    })

    // 格式刷
    G6.registerBehavior('format-brush', {
      getEvents() {
        return {
          'node:click': 'onClick',
        }
      },
      onClick(event: any) {
        const selectedItem = event.item
        const Graph: any = this.graph

        let nodeToChange: Array<any> = []

        if (!selectedItem.hasState('highlight')) {
          if (GraphAnalysis.formatType === 'both') {
            nodeToChange = selectedItem.getNeighbors()
          } else if (GraphAnalysis.formatType === 'source') {
            nodeToChange = selectedItem.getNeighbors('source')
            selectedItem.getOutEdges().forEach((edge: any) => {
              if (!edge.getModel().directed) {
                nodeToChange.push(edge.getTarget())
              }
            })
            nodeToChange.push(selectedItem)
          } else if (GraphAnalysis.formatType === 'target') {
            nodeToChange = selectedItem.getNeighbors('target')
            selectedItem.getInEdges().forEach((edge: any) => {
              if (!edge.getModel().directed) {
                nodeToChange.push(edge.getSource())
              }
            })
            nodeToChange.push(selectedItem)
          } else {
            const category = selectedItem.getModel().categoryId
            Graph.getNodes().forEach((node: any) => {
              if (node.getModel().categoryId === category) {
                nodeToChange.push(node)
              }
            })
          }
        } else {
          const nodes = Graph.findAllByState('node', 'highlight')
          if (GraphAnalysis.formatType !== 'category') {
            nodes.forEach((node: any) => {
              const partNodes = node.getNeighbors(
                GraphAnalysis.formatType === 'both'
                  ? undefined
                  : GraphAnalysis.formatType
              )
              if (GraphAnalysis.formatType === 'source') {
                node.getOutEdges().forEach((edge: any) => {
                  if (!edge.getModel().directed) {
                    partNodes.push(edge.getTarget())
                  }
                })
              } else if (GraphAnalysis.formatType === 'target') {
                node.getInEdges().forEach((edge: any) => {
                  if (!edge.getModel().directed) {
                    partNodes.push(edge.getSource())
                  }
                })
              }
              nodeToChange = [...nodeToChange, ...partNodes]
            })
            nodeToChange = [...nodeToChange, ...nodes]
          } else {
            const categories: Array<any> = []
            nodes.forEach((node: any) => {
              const category = node.getModel().categoryId
              if (!categories.includes(category)) {
                categories.push(category)
              }
            })
            Graph.getNodes().forEach((node: any) => {
              const category = node.getModel().categoryId
              if (categories.includes(category)) {
                nodeToChange.push(node)
              }
            })
          }
        }

        that.setNodeColor(Graph, nodeToChange, GraphAnalysis.formatColor)
      },
    })

    // 热区
    G6.registerBehavior('node-heatmap', {
      getEvents() {
        return {
          'node:click': 'onNodeClick',
        }
      },
      async onNodeClick(event: any) {
        GraphAnalysis.setGraphAnalysisLoading(true)
        await GraphAnalysis.getHeatmapResult({
          source: event.item._cfg.id,
        })
        GraphAnalysis.setGraphAnalysisLoading(false)
      },
    })
  }

  /**
   * 注册边交互行为
   */
  public registerEdgeBehavior() {
    // const that  = this

    // hover高亮边
    G6.registerBehavior('edge-hover', {
      getEvents() {
        return {
          'edge:mouseenter': 'onEdgeMouseenter',
          'edge:mouseleave': 'onEdgeMouseleave',
        }
      },
      onEdgeMouseenter(event: any) {
        const Graph: any = this.graph
        Graph.setItemState(event.item, 'hover', true)
      },
      onEdgeMouseleave(event: any) {
        const Graph: any = this.graph
        Graph.setItemState(event.item, 'hover', false)
      },
    })
  }

  /**
   * 注册画布交互行为
   */
  public registerCanvasBehavior() {
    const that = this

    // 点击画布取消选中
    G6.registerBehavior('node-unselect', {
      getEvents() {
        return {
          'canvas:click': 'onCanvasClick',
        }
      },
      onCanvasClick() {
        that.clearSelectedNodes(this.graph)
        that.clearSelectedEdges(this.graph)
        that.nodeFrom = null
        that.nodeTo = null
      },
    })
  }

  /**
   * 转换数据格式，为边和节点增加样式
   */
  public transformData() {
    const graphElement = this.$refs.graphAnalysisRoot as HTMLElement
    let viewCenter = {}

    if (!this.isNodesWithPosition) {
      viewCenter = {
        x: graphElement.offsetWidth / 2,
        y: graphElement.offsetHeight / 2,
      }
    }

    this.transformedData.nodes = this.data.nodes.map((node: any) => {
      return {
        ...node,
        ...viewCenter,
        style: {
          lineWidth: node.style.lineWidth,
          fill: node.style.fill,
          stroke: node.style.fill,
          labelCfg: node.labelCfg
            ? node.labelCfg
            : {
                textBaseline: 'middle',
                textAlign: 'center',
                fontSize: 12,
                fill: '#000000',
                // opacity: 1,
              },
        },
        stateStyles: {
          default: {
            lineWidth: node.style.lineWidth,
            fill: node.style.fill,
            stroke: node.style.fill,
          },
          hover: {
            lineWidth: node.style.lineWidth,
            stroke: getLightColor(node.style.fill, 0.5),
          },
          highlight: {
            lineWidth: node.style.lineWidth,
            stroke: getDarkColor(node.style.fill, 0.2),
            fill: node.style.fill,
          },
          dark: {
            lineWidth: node.style.lineWidth,
            fill: getLightColor(node.style.fill, 0.7),
            stroke: getLightColor(node.style.fill, 0.7),
          },
        },
      }
    })

    this.transformedData.edges = this.data.edges.map((edge: any) => {
      return {
        ...edge,
        type: edge.target === edge.source ? 'loop' : edge.type,
        style: {
          lineWidth: edge.style.lineWidth
            ? edge.style.lineWidth
            : edge.weight
            ? edge.weight
            : 1,
          endArrow: edge.style.endArrow || edge.directed,
          stroke: edge.style.stroke,
        },
      }
    })
  }

  private tooltip = new G6.Tooltip({
    itemTypes: ['node', 'edge'],
    offsetX: 10,
    offsetY: 10,
    getContent(event: any) {
      const model: any = event.item.getModel()
      let attributesDom: string = '<div class="attr-key-value"> -- 空 -- </div>'
      if (model.attrs.length > 0) {
        attributesDom = ''
        model.attrs.forEach((attribute: any) => {
          attributesDom += `<div class="attr-key-value">${attribute.key}: ${attribute.value}</div>`
        })
      }
      const isEdge: boolean = event.item.getType() === 'edge'
      return `
        <div class="tooltip-section">
          <div class="tooltip-title">${isEdge ? '边' : '节点'}属性</div>
          ${attributesDom}
        </div>
      `
    },
  })

  /**
   * 画布
   */
  public initGraphAnalysis() {
    // 注册各种behavior
    this.registerNodeBehavior()
    this.registerEdgeBehavior()
    this.registerCanvasBehavior()

    const graphElement = this.$refs.graphAnalysisRoot as HTMLElement
    GraphAnalysis.setGraphAnalysisRootSize({
      width: graphElement.offsetWidth,
      height: graphElement.offsetHeight,
    })
    this.analysisGraph = new G6.Graph({
      container: 'graph-analysis-root',
      width: graphElement.offsetWidth,
      height: graphElement.offsetHeight,
      layout: this.isNodesWithPosition ? {} : this.randomLayout,
      plugins: [this.tooltip],
      // plugins: [this.contextMenu, this.edgeContextMenu, this.nodeTooltip, this.edgeTooltip],
      animate: true,
      fitCenter: true,
      nodeStateStyles: {
        highlight: {
          lineWidth: 2,
          stroke: '#5B8FF9',
          fill: '#D2D2D2',
        },
        default: {
          lineWidth: 0,
          fill: '#D2D2D2',
        },
        hover: {
          cursor: 'pointer',
        },
      },
      edgeStateStyles: {
        hover: {
          lineWidth: 2,
          stroke: '#A8AEFF',
        },
        highlight: {
          lineWidth: 3,
          stroke: '#A8AEFF',
        },
      },
      modes: {
        default: ['drag-canvas', 'node-unselect', 'node-hover'], // 默认
        select: [
          'node-select',
          {
            type: 'drag-canvas',
            scalableRange: 1,
          },
          'node-unselect',
          'node-drag',
          'node-hover',
        ], // 箭头选择
        circleSelect: [
          'circle-select',
          'circle-drag',
          'node-hover',
          {
            type: 'drag-canvas',
            shouldBegin: () => false,
          },
        ], // 圆圈选择
        /*
        drag: ['node-drag', 'drag-canvas', 'zoom-canvas', 'node-unselect'], // 手型拖动
        circleDrag: [
          'circle-drag',
          'zoom-canvas',
          'node-unselect', // 圆圈拖动
          {
            type: 'drag-canvas',
            shouldBegin: () => false,
          },
        ],
        */
        brush: [
          // 框选
          'node-unselect',
          'node-drag',
          'node-hover',
          {
            type: 'brush-select',
            trigger: 'drag',
            includeEdges: false,
            onSelect: (nodes) => {
              this.clearSelectedNodes(this.analysisGraph)
              if (nodes && nodes.length > 0) {
                this.selectNodes(this.analysisGraph, nodes)
              }
            },
            onDeselect: () => {
              this.clearSelectedNodes(this.analysisGraph)
            },
          },
        ],
        lasso: [
          // 套索
          'node-unselect',
          'node-drag',
          'node-hover',
          {
            type: 'lasso-select',
            trigger: 'drag',
            includeEdges: false,
            onSelect: (nodes) => {
              this.clearSelectedNodes(this.analysisGraph)
              if (nodes && nodes.length > 0) {
                this.selectNodes(this.analysisGraph, nodes)
              }
            },
            onDeselect: () => {
              this.clearSelectedNodes(this.analysisGraph)
            },
          },
        ],
        shortestPath: [
          'shortest-path',
          {
            type: 'drag-canvas',
            scalableRange: 1,
          },
          'node-unselect',
          'node-hover',
        ], // 最短路径
        color: [
          {
            type: 'drag-canvas',
            scalableRange: 1,
          },
          'node-unselect',
          'node-color',
          'node-hover',
        ], // 着色
        circleColor: [
          {
            type: 'drag-canvas',
            scalableRange: 1,
          },
          'node-unselect',
          'circle-color',
          'node-hover',
        ], // 圆圈着色
        formatBrush: [
          {
            type: 'drag-canvas',
            scalableRange: 1,
          },
          'node-unselect',
          'format-brush',
          'node-hover',
        ], // 格式刷
        heatmap: [
          {
            type: 'drag-canvas',
            scalableRange: 1,
          },
          'node-unselect',
          'node-hover',
          'node-heatmap',
        ], // 热图
      },
      defaultEdge: {
        type: 'circle-running',
        style: {
          lineWidth: 2,
          stroke: '#D9D9D9',
          endArrow: false,
        },
        /*
        labelCfg: {
          style: {
            opacity: 0,
          },
        },
        */
      },
      defaultNode: {
        size: 20,
        style: {
          lineWidth: 0,
          fill: '#D2D2D2',
        },
        labelCfg: {
          position: 'center',
          // offset: 0,
          style: {
            // textAlign: 'center',
            // textBaseline: 'middle',
            fill: '#000',
            fontSize: 12,
          },
        },
      },
    })
    // TODO 暂时关闭关闭局部渲染， 解决 label edge 移动残影问题
    this.analysisGraph.get('canvas').set('localRefresh', false)

    this.analysisGraph.data(this.transformedData)
    this.analysisGraph.render()

    this.registerG6Layout() // 注册 基本弧线布局 和 环形弧线布局

    this.analysisGraph.setMode('select')
    this.analysisGraph.getNodes().forEach((node: any) => {
      this.analysisGraph.setItemState(node, 'default', true)
    })
    GraphAnalysis.setManualAction(false)
    this.changeLabelSettings(this.labelSettings)
    this.changeEdgeSettings(this.edgeSettings)

    this.handleGraphNodeEvent()
    this.handleGraphEdgeEvent()
    this.handleGraphCanvasEvent()
  }

  // 节点事件
  public handleGraphNodeEvent() {
    this.analysisGraph.on('node:mousemove', (event: any) => {
      this.mousePosition = [event.canvasX, event.canvasY]
    })

    // hover 更改节点的层级
    this.analysisGraph.on('node:mouseenter', (event: any) => {
      this.mousePosition = [event.canvasX, event.canvasY]
      event.item.toFront()
    })

    this.analysisGraph.on('canvas:drag', (event: any) => {
      this.mousePosition = [event.canvasX, event.canvasY]
    })
  }

  // 边事件
  public handleGraphEdgeEvent() {
    this.analysisGraph.on('edge:mouseenter', (event: any) => {
      if (this.edgeSettings.edgeLabelIsShow === 'highlightshow') {
        const container = event.item.getContainer()
        const children = container.get('children')
        children.forEach((child: any) => {
          if (child.cfg.type === 'text') {
            child.show()
          }
        })
      }
    })

    this.analysisGraph.on('edge:mouseleave', (event: any) => {
      if (this.edgeSettings.edgeLabelIsShow === 'highlightshow') {
        const container = event.item.getContainer()
        const children = container.get('children')
        children.forEach((child: any) => {
          if (child.cfg.type === 'text') {
            child.hide()
          }
        })
      }
    })
  }

  // 画布事件
  public handleGraphCanvasEvent() {
    this.analysisGraph.on('canvas:mouseenter', () => {
      this.mouseInCanvas = true
    })

    this.analysisGraph.on('canvas:mousemove', (event: any) => {
      this.mousePosition = [event.canvasX, event.canvasY]
    })

    this.analysisGraph.on('canvas:mouseout', () => {
      this.mouseInCanvas = false
    })

    this.analysisGraph.on('drag', (event: any) => {
      if (
        this.labelSettings.labelIsShow === 'highlightshow-inner' ||
        this.labelSettings.labelIsShow === 'highlightshow-outer'
      ) {
        this.analysisGraph.getNodes().forEach((node: any) => {
          if (!node.hasState('highlight')) {
            const container = node.getContainer()
            const children = container.get('children')
            children.forEach((child: any) => {
              if (child.cfg.type === 'text') {
                child.hide()
              }
            })
          }
        })
        this.analysisGraph
          .get('modeController')
          .currentBehaves.forEach((behavior: any) => {
            if (behavior.type === 'lasso-select') {
              const lassoContour = behavior.points.map((point: any) => [
                this.analysisGraph.getCanvasByPoint(point.x, point.y).x,
                this.analysisGraph.getCanvasByPoint(point.x, point.y).y,
              ])
              this.analysisGraph.getNodes().forEach((node: any) => {
                if (!node.isVisible()) {
                  return
                }
                if (isItemIntersecPolygon(node, lassoContour)) {
                  const container = node.getContainer()
                  const children = container.get('children')
                  children.forEach((child: any) => {
                    if (child.cfg.type === 'text') {
                      child.show()
                    }
                  })
                }
              })
            } else if (behavior.type === 'brush-select') {
              const { originPoint } = behavior
              const p1 = {
                x: event.x,
                y: event.y,
              }
              const p2 = this.analysisGraph.getPointByCanvas(
                originPoint.x,
                originPoint.y
              )
              const left = Math.min(p1.x, p2.x)
              const right = Math.max(p1.x, p2.x)
              const top = Math.min(p1.y, p2.y)
              const bottom = Math.max(p1.y, p2.y)

              this.analysisGraph.getNodes().forEach((node: any) => {
                if (!node.isVisible()) {
                  return
                }
                const bbox = node.getBBox()

                if (
                  bbox.centerX >= left &&
                  bbox.centerX <= right &&
                  bbox.centerY >= top &&
                  bbox.centerY <= bottom
                ) {
                  const container = node.getContainer()
                  const children = container.get('children')
                  children.forEach((child: any) => {
                    if (child.cfg.type === 'text') {
                      child.show()
                    }
                  })
                }
              })
            }
          })
      }
    })

    this.analysisGraph.on('wheel', (event: any) => {
      const { wheelDeltaY } = event
      if (wheelDeltaY > 0) {
        this.changeScale(this.scaleLevel + 0.05)
      } else if (wheelDeltaY < 0) {
        this.changeScale(this.scaleLevel - 0.05)
      }
    })
  }

  public selectNodes(graph: any, nodes: any, isDrag: boolean = false) {
    nodes?.forEach((node: any) => {
      let nodeSelected = [node]
      if (
        ((this.graphMode === 'select' || this.graphMode === 'circleSelect') &&
          GraphAnalysis.selectNeighbors &&
          !isDrag) ||
        ((this.selectedTool === '框选' || this.selectedTool === '套索') &&
          GraphAnalysis.lassoNeighbors)
      ) {
        nodeSelected = [...graph.getNeighbors(node), node]
      }
      nodeSelected.forEach((n: any) => {
        graph.setItemState(n, 'dark', false)
        graph.setItemState(n, 'highlight', true)
        if (
          GraphAnalysis.labelIsShow === 'highlightshow-inner' ||
          GraphAnalysis.labelIsShow === 'highlightshow-outer'
        ) {
          const container = n.getContainer()
          const children = container.get('children')
          children.forEach((child: any) => {
            if (child.cfg.type === 'text') {
              child.show()
            }
          })
        }
      })
    })
    graph.getNodes().forEach((node: any) => {
      if (!node.hasState('highlight')) {
        graph.setItemState(node, 'dark', true)
        if (
          GraphAnalysis.labelIsShow === 'highlightshow-inner' ||
          GraphAnalysis.labelIsShow === 'highlightshow-outer'
        ) {
          const container = node.getContainer()
          const children = container.get('children')
          children.forEach((child: any) => {
            if (child.cfg.type === 'text') {
              child.hide()
            }
          })
        }
      }
    })
    this.selectedNodes = [...this.selectedNodes, ...nodes]
  }

  public clearSelectedNodes(graph: any, clearCache: boolean = true) {
    graph.getNodes().forEach((node: any) => {
      graph.clearItemStates(node)
      graph.setItemState(node, 'default', true)
      if (
        GraphAnalysis.labelIsShow === 'highlightshow-inner' ||
        GraphAnalysis.labelIsShow === 'highlightshow-outer'
      ) {
        const container = node.getContainer()
        const children = container.get('children')
        children.forEach((child: any) => {
          if (child.cfg.type === 'text') {
            child.hide()
          }
        })
      }
    })
    if (clearCache) {
      this.selectedNodes = []
    }
  }

  public clearSelectedEdges(graph: any) {
    graph.getEdges().forEach((edge: any) => {
      graph.clearItemStates(edge)
    })
  }

  /**
   * 更新后进行保存
   */
  public async saveChangedData(nodes: Array<any>, edges: Array<any> = []) {
    const changedNodes = nodes.map((node: any) => {
      const model = node.getModel()
      let newX = model.x
      let newY = model.y

      if (this.scaleType === 'semantics') {
        const dx = (model.x - this.xCenter) / this.scaleLevel
        const dy = (model.y - this.yCenter) / this.scaleLevel
        newX = this.xCenter + dx
        newY = this.yCenter + dy

        // 修改数据
        this.data.nodes.forEach((n: any) => {
          if (n.id === model.id) {
            n.x = newX
            n.y = newY
          }
        })
      }

      return {
        id: model.id,
        orderId: model.orderId,
        label: model.label,
        labelCfg: model.labelCfg,
        categoryId: model.categoryId,
        size: model.size,
        type: model.type,
        x: newX,
        y: newY,
        style: {
          fill: model.style.fill,
          stroke: model.style.stroke,
          lineWidth: model.style.lineWidth,
        },
        attrs: model.attrs,
      }
    })

    const changedEdges = edges.map((edge: any) => {
      const model = edge.getModel()
      const dealEdge: any = {
        id: model.id,
        edgeId: model.edgeId,
        orderId: model.orderId,
        label: model.label,
        labelCfg: model.labelCfg,
        source: model.source,
        target: model.target,
        type: model.type,
        directed: model.directed,
        weight: model.weight,
        attrs: model.attrs,
        style: {
          endArrow: model.style.endArrow,
          lineWidth: model.style.lineWidth,
          stroke: model.style.stroke,
        },
      }
      // 弧线布局 边的弧线参数
      if (model.curveOffset) {
        dealEdge.curveOffset = model.curveOffset
      }
      if (model.controlPoints) {
        dealEdge.controlPoints = model.controlPoints
      }
      return dealEdge
    })

    await GraphAnalysis.updateGraphItems({
      projectId: this.projectId,
      graphId: this.graphId,
      needUndo: this.isNodesWithPosition,
      links: changedEdges,
      nodes: changedNodes,
      size: [this.analysisGraph.getWidth(), this.analysisGraph.getHeight()],
      actionContext: {
        selectedTool: GraphAnalysis.selectedTool,
        selectRadius: GraphAnalysis.selectRadius,
        selectNeighbors: GraphAnalysis.selectNeighbors,
        lassoNeighbors: GraphAnalysis.lassoNeighbors,
        colorRadius: GraphAnalysis.colorRadius,
        selectedColor: GraphAnalysis.selectedColor,
        formatColor: GraphAnalysis.formatColor,
        formatType: GraphAnalysis.formatType,
        redrawColor: GraphAnalysis.redrawColor,
        labelIsShow: GraphAnalysis.labelIsShow,
        labelFontType: GraphAnalysis.labelFontType,
        labelFontSize: GraphAnalysis.labelFontSize,
        labelColorType: GraphAnalysis.labelColorType,
        labelColor: GraphAnalysis.labelColor,
        edgeIsShow: GraphAnalysis.edgeIsShow,
        edgeLabelIsShow: GraphAnalysis.edgeLabelIsShow,
        edgeColorType: GraphAnalysis.edgeColorType,
        edgeColor: GraphAnalysis.edgeColor,
        heatmapColor: GraphAnalysis.heatmapColor,
      },
    })

    this.isNodesWithPosition = true
  }

  private generateNodeStyleCfg(color: string) {
    return {
      style: {
        fill: color,
        stroke: color,
      },
      stateStyles: {
        default: {
          stroke: color,
          fill: color,
        },
        hover: {
          stroke: getLightColor(color, 0.5),
          fill: color,
        },
        highlight: {
          stroke: getDarkColor(color, 0.2),
          fill: color,
        },
        dark: {
          fill: getLightColor(color, 0.7),
          stroke: getLightColor(color, 0.7),
        },
      },
    }
  }

  public setNodeColor(graph: any, nodes: any, color: string) {
    nodes.forEach((node: any) => {
      node.update(this.generateNodeStyleCfg(color))
    })
    if (this.edgeSettings.edgeColorType === 'node') {
      GraphAnalysis.setManualAction(false)
      this.changeEdgeSettings(this.edgeSettings)
      if (this.selectedTool !== '热区') {
        this.saveChangedData(nodes, this.analysisGraph.getEdges())
      }
    } else if (this.selectedTool !== '热区') {
      this.saveChangedData(nodes)
    }
  }

  // 键盘事件
  public globalKeyDown($event: KeyboardEvent) {
    // console.log($event)
    this.currentKeyboard = $event.key
  }

  public globalKeyUp() {
    // $event: KeyboardEvent
    // console.log($event)
    this.currentKeyboard = ''
  }

  /** ===== 修改 footer 高度  ===== */
  private mouseLastPosition = { x: 0, y: 0 }
  private dataTableHeight: number = 32
  private dataTableMinHeight: number = 32
  private dataTableMaxHeight: any = 1000

  // 折叠详情数据表
  public onChange() {
    this.tableSwitch = !this.tableSwitch
    this.$nextTick(() => {
      if (this.tableSwitch) {
        const toHeight: number = GraphAnalysis.tableInfoHeight
        if (toHeight <= 100) {
          this.dataTableHeight = 300
        } else {
          this.dataTableHeight = GraphAnalysis.tableInfoHeight
        }
      } else {
        this.dataTableHeight = this.dataTableMinHeight
      }
    })
  }

  public dragResizeTableHeight(value: any) {
    this.dataTableHeightDebounce(value)
  }

  private dataTableHeightDebounce = debounce(
    (height: any) => this.storeSaveHeight(height),
    400
  )

  public storeSaveHeight(height: number) {
    this.tableSwitch = height > 38
    GraphAnalysis.setTableInfoHeight(height)
  }

  /**
   * 改变 drawer 宽度
   */
  public resizeDrawerWidth($event: MouseEvent) {
    this.footerTransition = false
    $event.stopPropagation()
    this.dataTableMaxHeight = (this.$refs
      .analysisWindow as HTMLElement).getBoundingClientRect().height
    this.mouseLastPosition = { x: $event.screenX, y: $event.screenY }
    document.addEventListener('mouseup', this.dragMouseUpListener)
    document.addEventListener('mousemove', this.dragMouseMoveListener)
  }

  public dragMouseMoveListener({ screenX, screenY }: MouseEvent) {
    const dy = screenY - this.mouseLastPosition.y
    this.$nextTick(() => {
      if (
        this.dataTableHeight <= this.dataTableMaxHeight &&
        this.dataTableHeight >= this.dataTableMinHeight
      ) {
        let height: number = this.dataTableHeight - dy
        if (height < this.dataTableMinHeight) {
          height = this.dataTableMinHeight
        }
        if (height > this.dataTableMaxHeight) {
          height = this.dataTableMaxHeight
        }
        this.dataTableHeight = height
        this.dragResizeTableHeight(height)
        this.mouseLastPosition = { x: screenX, y: screenY }
      }
    })
  }

  public dragMouseUpListener() {
    document.removeEventListener('mousemove', this.dragMouseMoveListener)
    document.removeEventListener('mouseup', this.dragMouseUpListener)
  }

  private globalMouseUp() {
    this.footerTransition = true
    document.removeEventListener('mousemove', this.dragMouseMoveListener)
    document.removeEventListener('mouseup', this.dragMouseUpListener)
  }

  /**
   * 样式 / 布局 运行 $emit
   * @param runInfo
   * runInfo-type:  layout-run 布局运行 attribute-color-run 属性着色  sort-run 排序设置
   */
  public graphRun(runInfo: any) {
    if (this.analysisGraph.getNodes().length === 0) {
      this.$message.warning('图数据为空，请先载入图数据再进行操作！')
      return
    }
    if (runInfo.type === 'layout-run') {
      this.layoutRun(runInfo)
    } else if (runInfo.type === 'attribute-color-run') {
      this.coloringRun(runInfo)
    } else if (runInfo.type === 'sort-run') {
      this.sortRun(runInfo)
    }
  }

  /**
   * 属性着色运行
   */
  public coloringRun(runInfo: any) {
    GraphAnalysis.setGraphAnalysisLoadingText('着色中，请等待...')
    GraphAnalysis.setGraphAnalysisLoading(true)
    const { info } = runInfo
    this.$nextTick(() => {
      info.forEach(
        (attributeSorting: {
          color: string
          count: number
          nodeIds: any[]
          proportion: number
          value: any
        }) => {
          attributeSorting.nodeIds.forEach((id: any) => {
            this.analysisGraph.updateItem(
              id,
              this.generateNodeStyleCfg(attributeSorting.color)
            )
          })
          // 当边的颜色和节点一致的时候， 需要更新下边的颜色
          if (this.edgeSettings.edgeColorType === 'node') {
            this.changeEdgeSettings(this.edgeSettings)
          }
        }
      )
      this.delayCloseLoading(500) // 延时关闭 loading
      this.coloringOrSortRunEnd()
    })
  }

  /**
   * 排序设置运行
   */
  public sortRun(runInfo: any) {
    GraphAnalysis.setGraphAnalysisLoadingText('排序中，请等待...')
    GraphAnalysis.setGraphAnalysisLoading(true)
    const { sizeOrColor, info } = runInfo
    this.$nextTick(() => {
      // 大小
      if (sizeOrColor === 2) {
        const { minSetValue, maxSetValue } = info
        const minAndMaxDifferences: number = maxSetValue - minSetValue
        info.nodesWeight.forEach((node: { id: any; value: number }) => {
          // 颜色 排序 size 大小排序
          this.analysisGraph.updateItem(node.id, {
            size: minSetValue + node.value * minAndMaxDifferences,
          })
        })
        // this.analysisGraph.layout()  // 修改大小是否重新布局
      } else {
        const { currentColor } = info
        const lightColorBase: number = 0.2
        const reductionCoefficient: number = 1 - 0.2
        info.nodesWeight.forEach((node: { id: any; value: number }) => {
          const color: string = getLightColorFromBasedColor(
            currentColor,
            lightColorBase + node.value * reductionCoefficient
          )
          this.analysisGraph.updateItem(
            node.id,
            this.generateNodeStyleCfg(color)
          )
        })
        // 当边的颜色和节点一致的时候， 需要更新下边的颜色
        if (this.edgeSettings.edgeColorType === 'node') {
          this.changeEdgeSettings(this.edgeSettings)
        }
      }
      this.delayCloseLoading(500) // 延时关闭 loading
      this.changeLabelSettings(this.labelSettings)
      this.coloringOrSortRunEnd()
    })
  }

  /**
   * 布局运行前的准备， 打来loading , fitCenter 图居中
   */
  public layoutRun(runInfo: any) {
    this.analysisGraph.fitCenter()
    GraphAnalysis.setGraphAnalysisLoading(true)
    GraphAnalysis.setGraphAnalysisLoadingText('执行布局中，请等待...')
    runInfo.info.onLayoutEnd = () => {
      return this.layoutEnd()
    }
    setTimeout(() => {
      this.StartGraphLayout(runInfo)
    }, 200)
  }

  /**
   * 分类执行布局
   */
  public StartGraphLayout(runInfo: any) {
    switch (runInfo.info.type) {
      case BASIC_ARC: // 基本弧线布局
        this.analysisGraph.updateLayout(runInfo.info)
        break
      case CIRCULAR_ARC: // 环形弧线布局
        this.analysisGraph.updateLayout(runInfo.info)
        break
      case ROTATING: // 工具 - 旋转
        this.analysisGraph.updateLayout(runInfo.info)
        break
      case SCALE: // 工具 - 扩展 / 收缩
        this.analysisGraph.updateLayout(runInfo.info)
        break
      // case CONTRACTION:
      //   console.log('收缩')
      //   break
      case NOVERLAP: // 工具 - 避免节点重叠
        this.analysisGraph.updateLayout({
          ...runInfo.info,
          preventOverlap: true,
          gpuEnabled: false,
        })
        break
      // case LABEL_ADJUST:  // 工具 - 避免 label 重叠
      //   console.log('避免 label 重叠')
      //   break
      default: {
        this.analysisGraph.getEdges().forEach((edge: any) => {
          this.analysisGraph.updateItem(edge._cfg.id, {
            type: 'line',
          })
        })
        this.$nextTick(() => {
          if (runInfo.info.type === 'random') {
            this.analysisGraph.destroyLayout()
          }
          this.analysisGraph.updateLayout(runInfo.info)
          // this.analysisGraph.get('canvas').set('localRefresh', false)
        })
      }
    }
  }

  /**
   * 布局结束， 保存位置信息
   */
  public layoutEnd() {
    this.analysisGraph.destroyLayout()
    GraphAnalysis.setGraphAnalysisLoadingText('布局成功！')
    this.delayCloseLoading(700) // 延时关闭 loading
    setTimeout(() => {
      this.analysisGraph.fitCenter() // 布局结束后居中显示
    }, 600)
    this.saveChangedData(
      this.analysisGraph.getNodes(),
      this.analysisGraph.getEdges()
    )
  }

  /**
   * 属性着色 / 排序 后保存位置等信息
   */
  public coloringOrSortRunEnd() {
    this.$nextTick(() => {
      this.saveChangedData(
        this.analysisGraph.getNodes(),
        this.analysisGraph.getEdges()
      )
    })
  }

  /**
   *  自定义布局 注册
   */
  public registerG6Layout() {
    registerBasicArcG6Layout() // 基本弧线布局
    registerCircularArcG6Layout() // 环形弧线布局
    registerRotateLayoutTool() // 旋转 tool
    registerScaleLayout() // 缩放 tool
    registerNoverlapLayout() // 防止节点重叠 tool
  }

  /**
   * 比例缩放
   */
  // 画布缩放比例
  private scaleLevel: number = 1
  private scaleType: string = 'geometry'
  private xCenter: number = 0
  private yCenter: number = 0

  // 获取图的中心位置
  public updateGraphCenter() {
    const { nodes } = this.data
    let xMean = 0
    let yMean = 0
    const nodesLength = nodes.length
    nodes.forEach((node: any) => {
      xMean += node.x
      yMean += node.y
    })

    xMean /= nodesLength
    yMean /= nodesLength

    this.xCenter = xMean
    this.yCenter = yMean
  }

  // 处理缩放类型
  public handleScaleTypeChange(newScaleType: string) {
    this.scaleType = newScaleType

    if (newScaleType === 'geometry') {
      this.handleSemanticsScale(1)
    } else if (newScaleType === 'semantics') {
      // 重新获取数据中心
      this.updateGraphCenter()
      this.handleGeometryScale(1)
    }
    this.scaleLevel = 1 // 切换缩放方式后回到最初状态
    this.analysisGraph.fitCenter() // 布局结束后居中显示
  }

  // 缩放比例更改
  public changeScale(scale: any) {
    // 最小20%
    scale = Math.max(scale, 0.2)

    if (this.scaleType === 'geometry') {
      this.handleGeometryScale(scale)
    } else if (this.scaleType === 'semantics') {
      this.handleSemanticsScale(scale)
    }

    this.scaleLevel = scale
  }

  // 处理几何缩放
  public handleGeometryScale(scaleTo: any) {
    this.analysisGraph.zoomTo(scaleTo, {
      x: this.mousePosition[0],
      y: this.mousePosition[1],
    })
  }

  // 处理语义缩放
  public handleSemanticsScale(scale: any) {
    const nodes = this.analysisGraph.getNodes()
    nodes.forEach((node: any) => {
      const model = node.getModel()
      const { style, labelCfg } = model
      style.r = 10 / scale
      labelCfg.style.fontSize = 12 / scale
      node.update({
        style,
        labelCfg,
      })
    })

    this.analysisGraph.zoomTo(scale, {
      x: this.mousePosition[0],
      y: this.mousePosition[1],
    })
  }

  // 画布内容适应视口
  public fitView(padding: number) {
    this.analysisGraph.fitView(padding)
  }
}
</script>

<style scoped lang="less">
@import '@/components/graph-analysis/graph-analysis-section';

.select-circle {
  pointer-events: none;
}

.analysis-query-no-data {
  align-items: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  left: 0;
  position: absolute;
  top: 0;
  width: 100%;
}
</style>
